#include "CEntidad.hpp"

CEntidad::CEntidad()
{
	this->posActor = Vector3(0,0,0);
	postConstruccionComun();
}

CEntidad::CEntidad(Vector3 posActor)
{
	this->posActor = posActor;
	postConstruccionComun();
}

void CEntidad::postConstruccionComun()
{
	this->raizSceneNode = CAplicacion::getSingleton()->getSceneManager()->getRootSceneNode()->createChildSceneNode();
	this->raizSceneNode->translate(posActor.x , posActor.y , posActor.z);
	this->hijoSceneNode = NULL;
	this->actorFisica = NULL;
	tmpTranslacion.zero();
}

CEntidad::~CEntidad()
{

}

void CEntidad::anadirFormaCubo
(
	string nombreForma , 
	DENSIDAD densidad , 
	float ancho , 
	float alto , 
	float fondo,
	const NxVec3* initialVelocity,
	String texture
)
{
	if(CAplicacion::getSingleton()->getNxScene() == NULL) return;

	NxBodyDesc bodyDesc;
	bodyDesc.angularDamping	= 0.5f;
	bodyDesc.linearDamping = 0.0f;
	if(initialVelocity) bodyDesc.linearVelocity = *initialVelocity;

	NxBoxShapeDesc cuboDesc;
	cuboDesc.dimensions = NxVec3(ancho/2, alto/2, fondo/2);//Se divide por que dimensiones se miden desde el centro
	cuboDesc.localPose.t = NxVec3(0,alto/2,0);//Para dejar el cubo en el suelo
	cuboDesc.density = densidad;

	NxActorDesc actorDesc;
	actorDesc.shapes.pushBack(&cuboDesc);
	actorDesc.body		= &bodyDesc;
	actorDesc.density = densidad;
	actorDesc.globalPose.t  = CUtiles::toNxVec3(posActor);

	actorFisica = CAplicacion::getSingleton()->getNxScene()->createActor(actorDesc);
	postAnadirFormaComun();

	//Render
	hijoSceneNode = raizSceneNode->createChildSceneNode(nombreForma);
	hijoSceneNode->translate(0,alto/2,0);
	hijoSceneNode->scale(ancho/100 , alto/100 , fondo/100);
	ultimaEntidad = CAplicacion::getSingleton()->getSceneManager()->createEntity(nombreForma , CAplicacion::getSingleton()->getSceneManager()->PT_CUBE);
	//ultimaEntidad->setMaterialName("Ogre/Compositor/BrightPass2/");
	ultimaEntidad->setMaterialName(texture);
	hijoSceneNode->attachObject( ultimaEntidad );
}

void CEntidad::anadirFormaEsfera(string nombreForma , DENSIDAD densidad , float radio)
{
	//Fisica
	NxBodyDesc bodyDesc;
	bodyDesc.angularDamping	= 0.0f;
	bodyDesc.linearDamping = 0.0f;

	NxSphereShapeDesc esferaDesc;
	esferaDesc.radius		= radio;
	esferaDesc.density = densidad;
	esferaDesc.localPose.t = NxVec3(0,radio,0);

	NxActorDesc actorDesc;
	actorDesc.shapes.pushBack(&esferaDesc);
	actorDesc.body			= &bodyDesc;
	actorDesc.density		= densidad;
	actorDesc.globalPose.t  = CUtiles::toNxVec3(posActor);

	actorFisica = CAplicacion::getSingleton()->getNxScene()->createActor(actorDesc);
	postAnadirFormaComun();

	//Render
	hijoSceneNode = raizSceneNode->createChildSceneNode(nombreForma);
	hijoSceneNode->translate(0,radio,0);
	hijoSceneNode->scale(radio/45 , radio/45 , radio/45);
	ultimaEntidad = CAplicacion::getSingleton()->getSceneManager()->createEntity(nombreForma , CAplicacion::getSingleton()->getSceneManager()->PT_SPHERE);
	ultimaEntidad->setMaterialName("Material.001/SOLID");
	///ultimaEntidad->setNormaliseNormals(true);
	hijoSceneNode->attachObject( ultimaEntidad );
}

void CEntidad::anadirFormaMesh(string nombreForma , string rutaMesh , Quaternion orientacion , NxVec3 escalaRender , DENSIDAD densidad)
{
	//Render
	hijoSceneNode = raizSceneNode->createChildSceneNode(nombreForma);
	hijoSceneNode->scale(escalaRender.x , escalaRender.y , escalaRender.z);
	hijoSceneNode->setOrientation(orientacion);
	//hijoSceneNode->showBoundingBox(true);
	ultimaEntidad = CAplicacion::getSingleton()->getSceneManager()->createEntity(nombreForma ,  rutaMesh);
	float ancho = ultimaEntidad->getBoundingBox().getSize().x * escalaRender.x;
	float alto = ultimaEntidad->getBoundingBox().getSize().y * escalaRender.y;
	float fondo = ultimaEntidad->getBoundingBox().getSize().z * escalaRender.z;
	///ultimaEntidad->setNormaliseNormals(true);
	hijoSceneNode->attachObject( ultimaEntidad );

	//Fisica
	NxBodyDesc bodyDesc;
	bodyDesc.angularDamping	= 0.0f;
	bodyDesc.linearDamping = 0.0f;

	NxBoxShapeDesc cuboDesc;
	cuboDesc.dimensions = NxVec3(ancho/2, alto/2, fondo/2);//Se divide por que dimensiones se miden desde el centro
	cuboDesc.localPose.t = NxVec3(0,alto/2,0);//Para dejar el cubo en el suelo
	cuboDesc.density = densidad;

	NxActorDesc actorDesc;
	actorDesc.shapes.pushBack(&cuboDesc);
	actorDesc.density = densidad;
	actorDesc.body		= &bodyDesc;
	actorDesc.globalPose.t  = CUtiles::toNxVec3(posActor);

	actorFisica = CAplicacion::getSingleton()->getNxScene()->createActor(actorDesc);
	postAnadirFormaComun();
}

void CEntidad::anadirFormaPlano(string nombreForma , DENSIDAD densidad , float ancho , float fondo)
{
	//Fisica
	actorFisica = NULL;

	//Render
	hijoSceneNode = raizSceneNode->createChildSceneNode(nombreForma);
	hijoSceneNode->scale(ancho , 1.0f , fondo);
	Quaternion orientation;
	orientation.FromAngleAxis(Degree(-90),Vector3::UNIT_X);
	hijoSceneNode->setOrientation(orientation);
	ultimaEntidad = CAplicacion::getSingleton()->getSceneManager()->createEntity(nombreForma , CAplicacion::getSingleton()->getSceneManager()->PT_PLANE);
	ultimaEntidad->setMaterialName("Examples/GrassFloor");
	hijoSceneNode->attachObject( ultimaEntidad );
}

void CEntidad::anadirFormaSuelo(string nombreForma , DENSIDAD densidad , float ancho , float fondo)
{
	//Fisica
	NxPlaneShapeDesc planeDesc;
	planeDesc.d = 0;

	NxActorDesc actorDesc;
	actorDesc.shapes.pushBack(&planeDesc);
	actorFisica = CAplicacion::getSingleton()->getNxScene()->createActor(actorDesc);

	//Render
	Plane plane;
	plane.normal = Vector3::UNIT_Y;
	plane.d = 0;
	MeshManager::getSingleton().createPlane(nombreForma, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, plane,ancho,fondo,20,20,true,1,10,10,Vector3::UNIT_Z);
	ultimaEntidad = CAplicacion::getSingleton()->getSceneManager()->createEntity( nombreForma,nombreForma);
	//ultimaEntidad->setMaterialName("Examples/GrassFloor");
	ultimaEntidad->setMaterialName("Examples/G");
	hijoSceneNode = raizSceneNode->createChildSceneNode(nombreForma);
	hijoSceneNode->translate(ancho/2,0,fondo/2);
	hijoSceneNode->attachObject(ultimaEntidad);
}

void CEntidad::anadirFormaCapsula(string nombreForma , DENSIDAD densidad , float radio , float altura)
{
	//Fisica
	NxBodyDesc bodyDesc;
	bodyDesc.angularDamping	= 0.0f;
	bodyDesc.linearDamping = 0.0f;

	NxCapsuleShapeDesc capsuleDesc;
	capsuleDesc.density = densidad;
	capsuleDesc.height = altura;
	capsuleDesc.radius = radio;
	capsuleDesc.localPose.t = NxVec3(0,altura/2+radio,0);;

	NxActorDesc actorDesc;
	actorDesc.shapes.pushBack(&capsuleDesc);
	actorDesc.body = &bodyDesc;
	actorDesc.density = densidad;
	actorDesc.globalPose.t  = CUtiles::toNxVec3(posActor);

	actorFisica = CAplicacion::getSingleton()->getNxScene()->createActor(actorDesc);
	postAnadirFormaComun();

	//Render
	SceneNode * esferaArriba;
	SceneNode * esferabajo;

	esferaArriba = raizSceneNode->createChildSceneNode(nombreForma+"_esfera1");
	esferaArriba->translate(0,altura/2+radio,0);
	esferaArriba->translate(0,-altura/2,0);
	esferaArriba->scale(radio/45 , radio/45 , radio/45);
	Entity * actorRender = CAplicacion::getSingleton()->getSceneManager()->createEntity(nombreForma+"_esfera1" , CAplicacion::getSingleton()->getSceneManager()->PT_SPHERE);
	actorRender->setMaterialName("Material.001/SOLID");
	///actorRender->setNormaliseNormals(true);
	esferaArriba->attachObject( actorRender );

	hijoSceneNode = raizSceneNode->createChildSceneNode(nombreForma+"_cuerpo");
	hijoSceneNode->translate(0,altura/2+radio,0);
	hijoSceneNode->scale(radio , altura , radio);
	ultimaEntidad = CAplicacion::getSingleton()->getSceneManager()->createEntity(nombreForma+"_cuerpo" ,  "cilindro.mesh");
	///ultimaEntidad->setNormaliseNormals(true);
	hijoSceneNode->attachObject( ultimaEntidad );

	esferabajo = raizSceneNode->createChildSceneNode(nombreForma+"_esfera2");
	esferabajo->translate(0,altura/2+radio,0);
	esferabajo->translate(0,altura/2,0);
	esferabajo->scale(radio/45 , radio/45 , radio/45);
	Entity * actorRender3 = CAplicacion::getSingleton()->getSceneManager()->createEntity(nombreForma +"_esfera2", CAplicacion::getSingleton()->getSceneManager()->PT_SPHERE);
	actorRender3->setMaterialName("Material.001/SOLID");
	///actorRender3->setNormaliseNormals(true);
	esferabajo->attachObject( actorRender3 );
}

void CEntidad::postAnadirFormaComun()
{
	actorFisica->userData = (void *)this;
}

void CEntidad::quitarSombras()
{
	getUltimaEntidad()->setCastShadows( false );
}

/*
void CEntidad::reportarContacto(NxContactPairFlag flags)
{
	getActor()->setContactReportFlags(flags);
	getActor()->setContactReportThreshold(100);
}
*/

void CEntidad::refrescar(Real tiempoDesdeUltimoFrame)
{
	//Aplicar vector translacion
	if(actorFisica->readBodyFlag(NX_BF_KINEMATIC) && !tmpTranslacion.isZero())
	{
		actorFisica->moveGlobalPosition( actorFisica->getGlobalPosition() + actorFisica->getGlobalOrientation() * tmpTranslacion);
		tmpTranslacion.zero();
	}
	//PhysX le dice a OGRE la posicion y orientacion  de la entidad
	getRaizSceneNode()->setPosition(CUtiles::toVector3(actorFisica->getGlobalPosition()));
	getRaizSceneNode()->setOrientation(CUtiles::toQuaternion(actorFisica->getGlobalOrientationQuat()));
}

SceneNode * CEntidad::getHijoSceneNode()
{
	return hijoSceneNode;
}

SceneNode * CEntidad::getRaizSceneNode()
{
	return raizSceneNode;
}

void CEntidad::sinFisica()
{
	if(actorFisica!=NULL)
	{
		actorFisica->raiseBodyFlag(NX_BF_KINEMATIC);
	}
}

void CEntidad::conFisica()
{
	if(actorFisica!=NULL)
	{
		actorFisica->clearBodyFlag(NX_BF_KINEMATIC);
	}
}

void CEntidad::noPuedeVolcar()
{
    getActor()->raiseBodyFlag(NX_BF_FROZEN_ROT_X);
    getActor()->raiseBodyFlag(NX_BF_FROZEN_ROT_Z);
}

NxActor * CEntidad::getActor()
{
	return actorFisica;
}

Entity * CEntidad::getUltimaEntidad()
{
	return ultimaEntidad;
}

/*
NX_FORCE,                   //!< parameter has unit of mass * distance/ time^2, i.e. a force
NX_IMPULSE,                 //!< parameter has unit of mass * distance /time
NX_VELOCITY_CHANGE,	//!< parameter has unit of distance / time, i.e. the effect is mass independent: a velocity change.
NX_SMOOTH_IMPULSE,          //!< same as NX_IMPULSE but the effect is applied over all substeps. Use this for motion controllers that repeatedly apply an impulse.
NX_SMOOTH_VELOCITY_CHANGE,	//!< same as NX_VELOCITY_CHANGE but the effect is applied over all substeps. Use this for motion controllers that repeatedly apply an impulse.
NX_ACCELERATION				//!addLocalForce(f * CFrameListener::dt , tipoFuerza);
*/

void CEntidad::anadirTorsion(NxVec3 f , NxForceMode tipoFuerza)
{
	actorFisica->addLocalTorque(f * CFrameListener::dt , tipoFuerza);
}

void CEntidad::setAcelerar(float paso)
{
	tmpTranslacion += NxVec3(0,0,-paso);
}

void CEntidad::setFrenar(float paso)
{
	tmpTranslacion += NxVec3(0,0,paso);
}

void CEntidad::setSubir(float paso)
{
	tmpTranslacion += NxVec3(0,paso,0);
}

void CEntidad::setBajar(float paso)
{
	tmpTranslacion += NxVec3(0,-paso,0);
}

void CEntidad::setEstrafearDerecha(float paso)
{
	tmpTranslacion += NxVec3(paso,0,0);
}

void CEntidad::setEstrafearIzquierda(float paso)
{
	tmpTranslacion += NxVec3(-paso,0,0);
}

void CEntidad::setGiroYawAbsoluto(float radianes)
{
	NxMat33 orientacion( NX_IDENTITY_MATRIX );
	NxMat33 rotacionLocalY( NX_IDENTITY_MATRIX );

	rotacionLocalY.rotY( radianes );
	orientacion.multiply(orientacion , rotacionLocalY);

	actorFisica->setGlobalOrientation(orientacion);
}

void CEntidad::setGirarDerecha(float paso)
{
	NxMat33 orientacion = actorFisica->getGlobalOrientation();
	NxMat33 rotacionLocalY( NX_IDENTITY_MATRIX );

	rotacionLocalY.rotY( NxMath::degToRad(-paso) );
	orientacion.multiply(orientacion , rotacionLocalY);

	actorFisica->setGlobalOrientation(orientacion);
}

void CEntidad::setGirarIzquierda(float paso)
{
	NxMat33 orientacion = actorFisica->getGlobalOrientation();
	NxMat33 rotacionLocalY( NX_IDENTITY_MATRIX );

	rotacionLocalY.rotY( NxMath::degToRad(paso) );
	orientacion.multiply(orientacion , rotacionLocalY);

	actorFisica->setGlobalOrientation(orientacion);
}

void CEntidad::setMirarArriba(float paso)
{
	NxMat33 orientacion = actorFisica->getGlobalOrientation();
	NxMat33 rotacionLocalX( NX_IDENTITY_MATRIX );

	rotacionLocalX.rotX( NxMath::degToRad(paso) );
	orientacion.multiply(orientacion , rotacionLocalX);

	actorFisica->setGlobalOrientation(orientacion);
}

void CEntidad::setMirarAbajo(float paso)
{
	NxMat33 orientacion = actorFisica->getGlobalOrientation();
	NxMat33 rotacionLocalX( NX_IDENTITY_MATRIX );

	rotacionLocalX.rotX( NxMath::degToRad(-paso) );
	orientacion.multiply(orientacion , rotacionLocalX);

	actorFisica->setGlobalOrientation(orientacion);
}

void CEntidad::setInclinarMiradaDerecha(float paso)
{
	NxMat33 orientacion = actorFisica->getGlobalOrientation();
	NxMat33 rotacionLocalZ( NX_IDENTITY_MATRIX );

	rotacionLocalZ.rotZ( NxMath::degToRad(-paso) );
	orientacion.multiply(orientacion , rotacionLocalZ);

	actorFisica->setGlobalOrientation(orientacion);
}

void CEntidad::setInclinarMiradaIzquierda(float paso)
{
	NxMat33 orientacion = actorFisica->getGlobalOrientation();
	NxMat33 rotacionLocalZ( NX_IDENTITY_MATRIX );

	rotacionLocalZ.rotZ( NxMath::degToRad(-paso) );
	orientacion.multiply(orientacion , rotacionLocalZ);

	actorFisica->setGlobalOrientation(orientacion);
}

void CEntidad::setPlano(float x , float z)
{
	setX(x);
	setZ(z);
}

void CEntidad::setAltura(float y)
{
	setY(y);
}

float CEntidad::getX()
{
	return actorFisica->getGlobalPosition().x;
}

float CEntidad::getY()
{
	return actorFisica->getGlobalPosition().y;
}

float CEntidad::getZ()
{
	return actorFisica->getGlobalPosition().z;
}

NxVec3 CEntidad::getPos()
{
	//return matriz.GetTranslate();
	return actorFisica->getGlobalPosition();
}

void CEntidad::setX(float x)
{
	NxVec3 nuevaPos = actorFisica->getGlobalPosition();
	nuevaPos.x = x;
	actorFisica->setGlobalPosition(nuevaPos);
}

void CEntidad::setY(float y)
{
	NxVec3 nuevaPos = actorFisica->getGlobalPosition();
	nuevaPos.y = y;
	actorFisica->setGlobalPosition(nuevaPos);
}

void CEntidad::setZ(float z)
{
	NxVec3 nuevaPos = actorFisica->getGlobalPosition();
	nuevaPos.z = z;
	actorFisica->setGlobalPosition(nuevaPos);
}

string CEntidad::getNombre()
{
	return getHijoSceneNode()->getName();
}
